| Namespaces > Custom Attributes > Garbage Collection |
|
.NET
Framework assemblies are self-describing entities. They contain intermediate
code that is compiled to native machine instructions when the assembly is
loaded. More than that, assemblies contain a wealth of information about
that code. The compiler emits this descriptive information, or metadata,
into the assembly as it processes the source code. In traditional programming
environments, there is no way to access metadata once your code is compiled;
the information is lost during the compilation process. On the .NET Framework,
however, you have the ability to access metadata using runtime reflection
services.
Even
so, the compiler can only emit metadata for language keywords and constructs
that it knows about. The .NET Framework gives you the ability to extend the
metadata emitted by the compiler with your own descriptive attributes. These
customized attributes are somewhat analogous to language keywords, and are
stored with the other metadata in the assembly. This means you can access them
by using standard reflection services provided by the CLR.
Creating
a custom attribute is the same as declaring a class. The custom attribute class
has a constructor, and properties to set and retrieve state data. Custom
attributes must inherit from TCustomAttribute. The following code declares a
custom attribute with a constructor and two properties:
type
TCustomCodeAttribute
= class(TCustomAttribute)
private
Fprop1 : integer;
Fprop2 : integer;
aVal
: integer;
procedure Setprop1(p1 : integer);
procedure Setprop2(p2 : integer);
public
constructor Create(const myVal : integer);
property prop1 : integer read Fprop1 write
Setprop1;
property prop2 : integer read Fprop2 write
Setprop2;
end;
The
constructor would be implemented as usual:
constructor
TCustomCodeAttribute.Create(const myVal : integer);
begin
aVal
:= myVal;
end;
Place
the custom attribute directly before the class to which the attribute applies.
Note that Delphi for Microsoftâ .NET supports the use of named properties in the
initialization. These can be the names of properties, or of public fields of
the custom attribute class. Named properties are listed after all of the
parameters required by the constructor.
[TCustomCodeAttribute(1024, prop1=512,
prop2=128)]
TMyClass = class(TObject)
…
end;
The
first parameter, 1024, is the value required by the constructor. The second two
parameters are the properties defined in the custom attribute.
Custom
attributes applied to types can be detected at runtime with the
GetCustomAttributes method of the System.Type class. The following Delphi code
demonstrates how to query for custom attributes at runtime.
var
F:
TMyClass; // TMyClass declared
above
T:
System.Type;
A:
array of TObject; // Will hold custom
attributes
I:
Integer;
begin
F := TMyClass.Create;
T := F.GetType;
A := T.GetCustomAttributes(True);
// Write the type name, and then loop over
custom
// attributes returned from the call to
// System.Type.GetCustomAttributes.
Writeln(T.FullName);
for I := Low(A) to High(A) do
Writeln(A[I].GetType.FullName);
end.
You
can call unmanaged Win32 APIs (and other unmanaged code) by prefixing the
function declaration with the DllImport custom attribute. This attribute
resides in the System.Runtime.InteropServices namespace, as shown below:
Program HellowWorld2;
// Don't forget to include the
InteropServices unit when using the DllImport attribute.
uses System.Runtime.InteropServices;
[DllImport('user32.dll')]
function MessageBeep(uType : LongWord) :
Boolean; external;
begin
MessageBeep(LongWord(-1));
end.
Note
the external keyword is still required, to replace the block in the
function declaration. All other attributes, such as the calling convention, can
be passed through the DllImport custom attribute.
Delphi
syntax dictates that the GUID (if present) must immediately follow the
declaration of an interface. Since the GUID syntax is similar to that of custom
attributes, the compiler must be made to know the difference between a custom
attribute - which applies to the next declaration - and a GUID
specifier, which applies to the previous declaration. Without this
special case, the compiler would try to apply an attribute to the first member
of the interface.
When
the compiler sees an interface declaration, the next square bracket construct
found is assumed to be that of a GUID specifier for the interface. The GUID
must be in the traditional Delphi form:
['{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}']
Alternatively,
you can use the Guid custom attribute of the .NET Framework
(System.Runtime.InteropServices.GuidAttribute). If you choose this method, then
you introduce the attribute before the interface, as with any other custom
attribute.
The
effect in either case is the same: the GUID is emitted into the metadata for
the interface type. Note that GUIDs are not required for interfaces in the .NET
Framework. They are only used for COM interoperability.
For
the preview release of the Delphi for .NET compiler, custom attributes can only
be applied to types. They cannot yet be applied to methods, or method
parameters, or function results. The custom attribute scope resolution prefixes
(assembly, module, type, event, property, field, return, method, and param) are
not implemented. Nor is the AttributeUsageTargets enumeration. For the preview
release the attribute usage target is assumed to be All.
| Top | Contents | Copyrights | Borland Developer Network | Borland Home |